Fork me on GitHub
余鸢

Spring Security架构(二)

连接Spring Security架构(一)

创建和自定义过滤器链

Spring Boot应用程序(带有/ **请求匹配器的应用程序)中的默认回退过滤器链具有SecurityProperties.BASIC_AUTH_ORDER的预定义顺序。 你可以通过设置security.basic.enabled = false完全关闭它,或者你可以使用它作为回退,只是定义更低的顺序的其他规则。 为此,只需添加一个类型为WebSecurityConfigurerAdapter(或WebSecurityConfigurer)的@Bean,并使用@Order装饰类。 例:

1
2
3
4
5
6
7
8
9
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**")
...;
}
}

这个bean将导致Spring Security添加一个新的过滤器链,并在回退之前对其进行排序。

许多应用程序对于一组资源与另一组资源相比具有完全不同的访问规则。 例如,托管UI和后端API的应用程序可能支持基于cookie的身份验证,重定向到UI部分的登录页面,基于令牌的身份验证以及对API部分的未经身份验证的请求的401响应。 每组资源都有自己的WebSecurityConfigurerAdapter,它具有唯一的顺序和自己的请求匹配器。 如果匹配规则重叠,最早排序的过滤器链将取胜。

请求匹配用于分派和授权

安全过滤器链(或等效地,WebSecurityConfigurerAdapter)具有请求匹配器,用于决定是否将其应用于HTTP请求。 一旦做出应用特定过滤器链的决定,就不应用其他过滤器链。 但是在过滤器链中,您可以通过在HttpSecurity配置器中设置其他匹配器来对授权进行更细粒度的控制。 例:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**")
.authorizeRequests()
.antMatchers("/foo/bar").hasRole("BAR")
.antMatchers("/foo/spam").hasRole("SPAM")
.anyRequest().isAuthenticated();
}
}

配置Spring Security的最简单的错误之一是忘记这些匹配器适用于不同的进程,一个是整个过滤器链的请求匹配器,另一个是只选择要应用的访问规则。

将应用程序安全规则与执行器规则组合

如果您使用Spring Boot Actuator作为管理端点,你可能希望它们是安全的,默认情况下它们是。 事实上,只要将Actuator添加到安全应用程序中,就可以获得仅适用于执行器端点的附加过滤器链。 它使用仅匹配actuator 端点的请求匹配器定义,它具有ManagementServerProperties.BASIC_AUTH_ORDER的顺序,比默认的SecurityProperties回退过滤器少5个,因此在回退之前查询它。

如果希望应用程序安全规则应用于执行器端点,则可以添加一个比执行器端更早排序的过滤器链,以及包含所有执行器端点的请求匹配器。 如果您喜欢执行器端点的默认安全设置,那么最简单的方法是在执行器之后添加您自己的过滤器,但早于备用过滤器(例如ManagementServerProperties.BASIC_AUTH_ORDER + 1)。 例:

1
2
3
4
5
6
7
8
9
@Configuration
@Order(ManagementServerProperties.BASIC_AUTH_ORDER + 1)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**")
...;
}
}

注意:Web层中的Spring Security目前与Servlet API绑定,因此它只适用于在嵌入式或其他方式的servlet容器中运行应用程序。 但是,它不是绑定到Spring MVC或Spring web堆栈的其余部分,因此它可以用于任何servlet应用程序,例如使用JAX-RS。

Method Security

除了支持保护Web应用程序,Spring Security还支持将访问规则应用于Java方法执行。 对于Spring Security,这只是一种不同类型的“受保护资源”。 对于用户,这意味着访问规则使用相同格式的ConfigAttribute字符串(例如角色或表达式)声明,但在代码中的不同位置。 第一步是启用方法安全性,例如在我们的应用程序的顶级配置中:

1
2
3
4
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {
}

然后我们可以直接装饰方法资源,例如。

1
2
3
4
5
6
7
8
9
@Service
public class MyService {
@Secured("ROLE_USER")
public String secure() {
return "Hello Security";
}
}

此示例是具有安全方法的服务。 如果Spring创建了这种类型的@Bean,那么它将被代理,调用者必须在实际执行方法之前通过一个安全拦截器。 如果访问被拒绝,调用者将获得一个AccessDeniedException,而不是实际的方法结果。

还有其他注解可以用于强制实施安全约束的方法,特别是@PreAuthorize@PostAuthorize,它们允许你编写分别包含对方法参数和返回值的引用的表达式。

Tip 组合Web安全和方法安全性并不罕见。 过滤器链提供用户体验功能,如身份验证和重定向到登录页面等,方法安全性提供了更细致的保护。

使用线程

Spring Security基本上是线程绑定的,因为它需要使当前的认证主体可用于各种下游消费者。 基本构造块是SecurityContext,它可以包含Authentication (当用户登录时,它将是显式authenticatedAuthentication )。 您可以随时通过SecurityContextHolder中的静态方便方法访问和操作SecurityContext,而这又会简单地操纵TheAdLocal,例如。

1
2
3
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
assert(authentication.isAuthenticated);

这样做是不常见的用户应用程序代码,但它是有用的,例如,如果你需要编写一个自定义authentication过滤器(虽然即使在Spring Security中有基类,可以在你避免需要的地方使用SecurityContextHolder)。

如果需要访问Web端点中当前已验证的用户,则可以在@RequestMapping中使用方法参数。 例如

1
2
3
4
@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {
... // do stuff with user
}

此注解将当前的AuthenticationSecurityContext中拉出,并调用其上的getPrincipal() 方法以产生方法参数。 AuthenticationPrincipal的类型取决于用于验证身份验证的AuthenticationManager,因此获取对用户数据的类型安全引用是一个有用的小技巧。

如果使用Spring Security,HttpServletRequest中的Principal将是Authentication类型,因此你也可以直接使用:

1
2
3
4
5
6
@RequestMapping("/foo")
public String foo(Principal principal) {
Authentication authentication = (Authentication) principal;
User = (User) authentication.getPrincipal();
... // do stuff with user
}

这有时可能有用,如果你需要编写代码工作时,不使用Spring Security(你需要更加防御加载Authentication类)。

异步处理安全方法

由于SecurityContext是线程绑定的,如果你想做任何调用安全方法的后台处理,例如。 与@Async,你需要确保上下文传播。 这归结为将SecurityContext包装成在后台执行的任务(RunnableCallable等)。 Spring Security提供了一些帮助,使这更容易,例如RunnableCallable的包装。 要将SecurityContext传播到@Async方法,您需要提供一个AsyncConfigurer并确保Executor是正确的类型:

1
2
3
4
5
6
7
8
9
@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
}
}

注:翻译自https://spring.io/guides/topicals/spring-security-architecture/